/*************************************************************************
*File Name: ihttplib.h
*Author: 侯广臣
*2801059691@qq.com
*Created Time: Tue 19 Jan 2021 02:46:57 PM CST
 ************************************************************************/
#ifndef _IHTTPLIB_H_
#define _IHTTPLIB_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "idebug.h"
//C_HttpReader类以外函数所需要的头文件
#include <sys/sendfile.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <fcntl.h>

//分别是行正确，行错误，行不完整
enum CHECK_LINE_STATUS{CHECK_LINE_OK = 0, CHECK_LINE_BAD, CHECK_LINE_INCOMPLETE};

#define RECV_BUFFER_SIZE 4096
#define HTTP_CONTENT_SIZE 256

class C_HttpReader
{
    private:
        int m_sockfd;       //客户端套接字
        int m_recvBufferSize; //读取内容缓存地址长度
        char *m_recvBuffer;     //读取内容缓存地址

        int m_httpContentSize;    //m_httpContent总长度
        char **m_httpContent;    //以指针字符串的形式存储http请求中除请求行以外内容
        
        char *m_method;     //http请求方法
        char *m_url;        //httpURL
        char *m_version;    //http版本

        int m_checkIndex;   //检查到哪个字节
        int m_readLength;   //读取内容长度
        int m_lineCount;    //读取了多少行

    public:
        //默认构造函数，初始化参数值，动态分配内存
        C_HttpReader(int sockfd = -1, int recvBufferSize = RECV_BUFFER_SIZE,
                      int httpContentSize = HTTP_CONTENT_SIZE)
            :m_sockfd(sockfd), m_recvBufferSize(recvBufferSize), m_recvBuffer(nullptr),
             m_httpContentSize(httpContentSize), m_httpContent(nullptr),
             m_method(nullptr), m_url(nullptr), m_version(nullptr),
             m_checkIndex(0), m_readLength(0), m_lineCount(0)
        {
            m_recvBuffer = (char *)malloc(m_recvBufferSize);
            if(m_recvBuffer == nullptr)
            {
                #ifdef IDEBUG
                    PrintStdErrorInfo("malloc");
                #endif
                exit(-1);   
            }
            m_httpContent = (char**)malloc(sizeof(char*) * m_httpContentSize);
            if(m_httpContent == nullptr)
            {
                #ifdef IDEBUG
                    PrintStdErrorInfo("malloc");
                #endif
                exit(-1);
            }
        }
        //初始化参数值
        void Init(int sockfd)
        {
            m_sockfd = sockfd; m_checkIndex = 0; m_readLength = 0; m_lineCount = 0;
            m_method = nullptr; m_url = nullptr; m_version = nullptr;
        }
        //析构函数，释放资源
        ~C_HttpReader()
        {
            if(m_recvBuffer != nullptr) free(m_recvBuffer);
            if(m_httpContent != nullptr) free(m_httpContent);
        }
        //打印接收到的信息
        void PrintInfo(void)
        {
            //printf("m_method = %s, m_url = %s, m_version = %s\n", m_method, m_url, m_version);
            printf("%s %s %s\n", m_method, m_url, m_version);
            for(int i = 0; i < m_lineCount; i++)
            {
                //printf("第%d行: %s\n", i + 1, m_httpContent[i]);
                printf("%s\n", m_httpContent[i]);
            }
        }
        //获取请求内容函数
        char* GetUrl(void){ return m_url; }
        char* GetMethod(void){ return m_method; }
        char* GetVersion(void){ return m_version; }
        char** GetContent(void){ return m_httpContent; }
        int GetLineCount(void){ return m_lineCount; }
        //以下三个函数为主要功能函数，用于读取并判断请求是否正确
        //读取客户端发送文件，分析，保存信息
        bool Run(void);
        //解析请求行
        bool ParseRequestLine(char *buffer);
        //检查行是否完整正确
        CHECK_LINE_STATUS CheckLine(void);

};


//HTTP请求方法的枚举类型
enum METHOD_NUMBER{
	METHOD_BAD = 0,	/*请求错误*/
	METHOD_GET, METHOD_POST, METHOD_HEAD, METHOD_OPTIONS,
	METHOD_PUT, METHOD_PATCH, METHOD_DELETE, METHOD_TRACE, METHOD_CONNECT
};
//返回请求所代表的整数值，取值范围为 enum NETHOD 所包含类型
METHOD_NUMBER MethodNumber(const char *method);

//如果解析后请求为错误，则调用标准请求错误，sockfd: 客户端套接字
//返回发送的字节数，如果错误返回 -1
int SendBadResponse(int sockfd);

//解析正确后发送的响应头，sockfd: 客户端套接字 contentSzie: 响应体字节数 contentType: 响应体类型
//返回发送的字节数，如果错误返回 -1
int SendResponseHead(int sockfd, int contentSize, const char *contentType);

//发送响应体，sockfd: 客户端套接字 fileName: 需要发送给客户端的文件
//返回发送的字节数，如果错误返回 -1
int SendResponseContent(int sockfd, const char *fileName);

//发送Get请求的回应，错误反胃 -1
int SendGetResponse(int sockfd, const char *filePath, const char *url);

//把字符串str1, str2连接填充到catString中
//返回填充好的字符串，错误返回 NULL
char* StringCat(char catString[], const char *str1, const char *str2);
#endif
